package com.ruyiadmin.springcloud.producer.controller.business.module;

import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.ruyiadmin.springcloud.producer.common.annotations.core.AllowAnonymous;
import com.ruyiadmin.springcloud.producer.common.annotations.system.Log;
import com.ruyiadmin.springcloud.producer.common.annotations.system.Permission;
import com.ruyiadmin.springcloud.producer.common.beans.system.SystemConfig;
import com.ruyiadmin.springcloud.producer.common.beans.system.SystemRedisConfig;
import com.ruyiadmin.springcloud.producer.common.components.core.RuYiRedisComponent;
import com.ruyiadmin.springcloud.producer.common.core.business.enums.OperationType;
import com.ruyiadmin.springcloud.producer.common.core.business.enums.YesNo;
import com.ruyiadmin.springcloud.producer.common.core.system.entities.ActionResult;
import com.ruyiadmin.springcloud.producer.common.core.system.entities.QueryCondition;
import com.ruyiadmin.springcloud.producer.common.core.system.entities.QueryResult;
import com.ruyiadmin.springcloud.producer.common.exceptions.RuYiAdminCustomException;
import com.ruyiadmin.springcloud.producer.common.utils.core.RuYiAesUtil;
import com.ruyiadmin.springcloud.producer.common.utils.core.RuYiFileUtil;
import com.ruyiadmin.springcloud.producer.common.utils.core.RuYiPoiUtil;
import com.ruyiadmin.springcloud.producer.common.utils.core.RuYiRsaUtil;
import com.ruyiadmin.springcloud.producer.domain.dto.business.module.BizUserExcelDTO;
import com.ruyiadmin.springcloud.producer.domain.dto.business.module.BizUserModuleDTO;
import com.ruyiadmin.springcloud.producer.domain.dto.business.module.UnifiedAuthorizationDTO;
import com.ruyiadmin.springcloud.producer.domain.dto.system.ImportConfigDTO;
import com.ruyiadmin.springcloud.producer.domain.dto.system.LoginDTO;
import com.ruyiadmin.springcloud.producer.domain.entity.business.module.BizModule;
import com.ruyiadmin.springcloud.producer.domain.entity.business.module.BizUser;
import com.ruyiadmin.springcloud.producer.domain.entity.business.module.BizUserModule;
import com.ruyiadmin.springcloud.producer.service.iservices.business.module.IBizModuleService;
import com.ruyiadmin.springcloud.producer.service.iservices.business.module.IBizUserModuleService;
import com.ruyiadmin.springcloud.producer.service.iservices.business.module.IBizUserService;
import com.ruyiadmin.springcloud.producer.service.iservices.system.ISysImportConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.http.HttpStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.Valid;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * <p>
 * 模块与用户关系表 前端控制器
 * </p>
 *
 * @author RuYiAdmin
 * @since 2022-07-12
 */
@RestController
@RequestMapping("/BizUserModuleManagement")
@Api(tags = "系统模块与用户关系管理服务")
@RequiredArgsConstructor
public class BizUserModuleController {

    //region 关系服务私有属性

    private final SystemRedisConfig systemRedisConfig;
    private final SystemConfig systemConfig;
    private final RuYiRedisComponent redisUtils;
    private final IBizModuleService bizModuleService;
    private final IBizUserService bizUserService;
    private final IBizUserModuleService bizUserModuleService;
    private final ISysImportConfigService importConfigService;

    //endregion

    //region 查询模块用户列表

    @PostMapping("/Post")
    @ApiOperation(value = "查询模块用户列表")
    @Log(OperationType = OperationType.QueryList)
    @Permission(permission = "user:module:list")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public QueryResult<BizUserModuleDTO> queryByPage(@RequestBody QueryCondition queryCondition)
            throws ExecutionException, InterruptedException {
        CompletableFuture<QueryResult<BizUserModuleDTO>> future = CompletableFuture.supplyAsync(() ->
                this.bizUserModuleService.queryUserModule(queryCondition));
        return future.get();
    }

    //endregion

    //region 查询模块用户信息

    @GetMapping("/GetById/{id}")
    @ApiOperation(value = "查询模块用户信息")
    @Log(OperationType = OperationType.QueryEntity)
    @Permission(permission = "user:module:list")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult getById(@PathVariable("id") String id) throws ExecutionException, InterruptedException {
        CompletableFuture<ActionResult> future = CompletableFuture.supplyAsync(() ->
        {
            QueryCondition queryCondition = new QueryCondition();
            queryCondition.setPageIndex(0);
            queryCondition.setPageSize(10000);

            BizUserModuleDTO bizUserModuleDTO = this.bizUserModuleService
                    .queryUserModule(queryCondition)
                    .getList()
                    .stream()
                    .filter(t -> t.getId().equals(id))
                    .collect(Collectors.toList())
                    .get(0);

            return ActionResult.success(bizUserModuleDTO);
        });
        return future.get();
    }

    //endregion

    //region 新增模块用户信息

    @PostMapping("/Add")
    @ApiOperation(value = "新增模块用户信息")
    @Log(OperationType = OperationType.AddEntity)
    @Permission(permission = "user:module:add")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult add(@RequestBody BizUserModuleDTO bizUserModule) throws Exception {//@Valid
        BizModule bizModule = this.bizModuleService.getById(bizUserModule.getModuleId());

        String userLogonName = StrUtil.format("{}_{}",
                bizModule.getModuleShortNameEn(), bizUserModule.getUserLogonName());
        List<BizUser> bizUsers = this.bizUserService.queryAllBizUsers();
        long count = bizUsers.stream().filter(t -> t.getUserLogonName().equals(userLogonName)).count();
        if (count > 0) {
            throw new RuYiAdminCustomException("logon name has existed");
        }

        String defaultPassword = this.systemConfig.getDefaultPassword();
        String aesKey = this.systemConfig.getAesKey();

        //region 新增业务用户

        BizUser bizUser = new BizUser();
        bizUser.setUserLogonName(userLogonName);
        bizUser.setUserDisplayName(bizUserModule.getUserDisplayName());
        String salt = UUID.randomUUID().toString();
        bizUser.setSalt(salt);
        bizUser.setUserPassword(RuYiAesUtil.encrypt(defaultPassword + salt, aesKey));
        bizUser.setTelephone(bizUserModule.getTelephone());
        bizUser.setMobilephone(bizUserModule.getMobilephone());
        bizUser.setEmail(bizUserModule.getEmail());
        bizUser.setSex(bizUserModule.getSex());
        bizUser.setIsEnabled(bizUserModule.getIsEnabled());
        this.bizUserService.save(bizUser);

        //endregion

        //region 新增模块与用户关系

        BizUserModule userModule = new BizUserModule();
        userModule.setUserId(bizUser.getId());
        userModule.setModuleId(bizModule.getId());
        userModule.setUserModuleLogonName(bizUserModule.getUserModuleLogonName());
        userModule.setUserModulePassword(RuYiRsaUtil.decrypt(bizUserModule.getUserModulePassword(),
                this.systemConfig.getRsaPrivateKey()));
        this.bizUserModuleService.save(userModule);

        //endregion

        return ActionResult.ok();
    }

    //endregion

    //region 编辑模块用户信息

    @PutMapping("/Put")
    @ApiOperation(value = "编辑模块用户信息")
    @Log(OperationType = OperationType.EditEntity)
    @Permission(permission = "user:module:edit")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult edit(@Valid @RequestBody BizUserModuleDTO bizUserModule) throws Exception {
        BizUser bizUser = this.bizUserService.getById(bizUserModule.getUserId());
        if (bizUser != null) {
            BizModule bizmodule = this.bizModuleService.getById(bizUserModule.getModuleId());

            bizUser.setUserDisplayName(bizUserModule.getUserDisplayName());
            String password = RuYiRsaUtil.decrypt(bizUserModule.getUserPassword(),
                    this.systemConfig.getRsaPrivateKey());
            if (!password.equals(bizUser.getUserPassword())) {
                String aesKey = this.systemConfig.getAesKey();
                String salt = UUID.randomUUID().toString();
                bizUser.setSalt(salt);
                bizUser.setUserPassword(RuYiAesUtil.encrypt(password + salt, aesKey));
            }
            bizUser.setTelephone(bizUserModule.getTelephone());
            bizUser.setMobilephone(bizUserModule.getMobilephone());
            bizUser.setEmail(bizUserModule.getEmail());
            bizUser.setSex(bizUserModule.getSex());
            bizUser.setIsEnabled(bizUserModule.getIsEnabled());
            this.bizUserService.updateById(bizUser);

            List<BizUserModule> list = this.bizUserModuleService.list();
            BizUserModule userModule = list.stream()
                    .filter(t -> t.getUserId().equals(bizUser.getId()))
                    .filter(t -> t.getModuleId().equals(bizmodule.getId()))
                    .collect(Collectors.toList())
                    .get(0);
            userModule.setUserModuleLogonName(bizUserModule.getUserModuleLogonName());
            userModule.setUserModulePassword(RuYiRsaUtil.decrypt(bizUserModule.getUserModulePassword(),
                    this.systemConfig.getRsaPrivateKey()));
            this.bizUserModuleService.updateById(userModule);

            return ActionResult.ok();
        } else {
            throw new RuYiAdminCustomException("not found");
        }
    }

    //endregion

    //region 批量删除用户关系

    @DeleteMapping("/DeleteRange/{ids}")
    @ApiOperation(value = "批量删除用户关系")
    @Log(OperationType = OperationType.RemoveUnifiedAuthorization)
    @Permission(permission = "user:module:del")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult deleteRange(@PathVariable("ids") String ids) {
        List<String> array = Arrays.asList(ids.split(","));
        return ActionResult.success(this.bizUserModuleService.removeByIds(array));
    }

    //endregion

    //region 导入模块用户信息

    @PostMapping("/Import")
    @ApiOperation(value = "导入模块用户信息")
    @Log(OperationType = OperationType.ImportData)
    @Permission(permission = "user:module:import")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult importBizUsers(MultipartFile file, String moduleId) throws Exception {
        ActionResult actionResult = new ActionResult();
        int errorCount = 0;
        //region 常规合法性校验

        String id = UUID.randomUUID().toString();
        String filePath = RuYiFileUtil.saveMultipartFile(file, id);
        ImportConfigDTO configDTO = this.importConfigService.getImportConfig("BizUserImportConfig");
        configDTO.setExcelPath(filePath);
        errorCount = configDTO.validationDetecting();

        //endregion
        if (errorCount > 0) {
            //region 常规校验出不合规项

            actionResult.setObject(errorCount);
            id = FileNameUtil.getName(configDTO.getExcelPath()).split("\\.")[0];
            actionResult.setMessage(id);

            //endregion
        } else {
            //region 执行业务校验

            BizModule bizmodule = this.bizModuleService.getById(moduleId);
            List<BizUser> users = this.bizUserService.queryAllBizUsers();

            FileInputStream fileStream = new FileInputStream(configDTO.getExcelPath());
            Workbook workbook = new HSSFWorkbook(fileStream);
            Sheet worksheet = workbook.getSheetAt(0);

            for (int i = configDTO.getStartRow(); i <= worksheet.getLastRowNum(); i++) {
                String logonName = RuYiPoiUtil.getCellValue(worksheet.getRow(i).getCell(2)).trim();
                String userLogonName = StrUtil.format("{}_{}",
                        bizmodule.getModuleShortNameEn(), logonName);
                if (!StringUtils.isEmpty(logonName) &&
                        users.stream().anyMatch(t -> t.getUserLogonName().equals(userLogonName))) {
                    errorCount++;
                    RuYiPoiUtil.setCellComment(worksheet, i, 2, "用户已存在！");
                }
            }

            //endregion
            if (errorCount > 0) {
                //region 常规校验出不合规项

                id = UUID.randomUUID().toString();
                String path = configDTO.getExcelPath();
                configDTO.setExcelPath(RuYiFileUtil.copyFile(path, id));

                FileOutputStream outputStream = new FileOutputStream(configDTO.getExcelPath());
                workbook.write(outputStream);

                workbook.close();
                outputStream.close();
                fileStream.close();

                actionResult.setObject(errorCount);
                actionResult.setMessage(id);

                //endregion
            } else {
                //region 执行数据导入

                InputStream inputStream = file.getInputStream();
                ExcelReader reader = ExcelUtil.getReader(inputStream);

                HashMap<String, String> headerAlias = new HashMap<>(7);
                headerAlias.put("用户姓名", "userDisplayName");
                headerAlias.put("登录账号", "userLogonName");
                headerAlias.put("性别", "sex");
                headerAlias.put("密码密文", "userPassword");
                headerAlias.put("手机号", "mobilephone");
                headerAlias.put("座机号", "telephone");
                headerAlias.put("电子邮件", "email");
                reader.setHeaderAlias(headerAlias);

                List<BizUserExcelDTO> list = reader.readAll(BizUserExcelDTO.class);

                String defaultPassword = this.systemConfig.getDefaultPassword();
                String aesKey = this.systemConfig.getAesKey();

                list.forEach(item -> {
                    BizUser user = new BizUser();

                    String userLogonName = item.getUserLogonName();
                    String userPassword = item.getUserPassword();

                    String logonName = StrUtil.format("{}_{}",
                            bizmodule.getModuleShortNameEn(), userLogonName);
                    user.setUserLogonName(logonName);
                    user.setUserDisplayName(item.getUserDisplayName());

                    String salt = UUID.randomUUID().toString();
                    user.setSalt(salt);
                    user.setUserPassword(RuYiAesUtil.encrypt(defaultPassword + salt, aesKey));

                    user.setTelephone(item.getTelephone());
                    user.setMobilephone(item.getMobilephone());
                    user.setEmail(item.getEmail());
                    switch (item.getSex()) {
                        case "男":
                            user.setSex(0);
                            break;
                        case "女":
                            user.setSex(1);
                            break;
                        case "第三性别":
                            user.setSex(2);
                            break;
                    }
                    user.setIsEnabled(YesNo.YES.ordinal());
                    this.bizUserService.save(user);//保存模块与业务

                    BizUserModule bizUserModule = new BizUserModule();
                    bizUserModule.setUserId(user.getId());
                    bizUserModule.setModuleId(bizmodule.getId());
                    bizUserModule.setUserModuleLogonName(userLogonName);
                    bizUserModule.setUserModulePassword(userPassword);
                    this.bizUserModuleService.save(bizUserModule);//保存模块与业务用户关系
                });

                //endregion
            }
        }
        actionResult.setHttpStatusCode(HttpStatus.OK.value());
        return actionResult;
    }

    //endregion

    //region 用户一对多访问授权

    @PostMapping("/Grant")
    @ApiOperation(value = "用户一对多访问授权")
    @Log(OperationType = OperationType.UnifiedAuthorization)
    @Permission(permission = "user:module:grant,user:nonmodule:grant")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult grant(@Valid @RequestBody BizUserModule bizUserModule) throws Exception {
        List<BizUserModule> list = this.bizUserModuleService.list();
        long count = list.stream()
                .filter(t -> t.getUserId().equals(bizUserModule.getUserId()))
                .filter(t -> t.getModuleId().equals(bizUserModule.getModuleId()))
                .count();
        if (count <= 0) {
            BizUserModule userModule = new BizUserModule();
            userModule.setUserId(bizUserModule.getUserId());
            userModule.setModuleId(bizUserModule.getModuleId());
            userModule.setUserModuleLogonName(bizUserModule.getUserModuleLogonName());
            userModule.setUserModulePassword(RuYiRsaUtil.decrypt(bizUserModule.getUserModulePassword(),
                    this.systemConfig.getRsaPrivateKey()));
            this.bizUserModuleService.save(userModule);
            return ActionResult.ok();
        }
        throw new RuYiAdminCustomException("relationship exited");
    }

    //endregion

    //region 禁用业务模块用户

    @GetMapping("/DisableUser/{id}")
    @ApiOperation(value = "禁用业务模块用户")
    @Log(OperationType = OperationType.EditEntity)
    @Permission(permission = "user:nonmodule:grant")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult disableUser(@PathVariable("id") String id) {
        BizUser user = this.bizUserService.getById(id);
        user.setIsEnabled(YesNo.NO.ordinal());
        this.bizUserService.updateById(user);
        return ActionResult.ok();
    }

    //endregion

    //region 删除业务模块用户

    @DeleteMapping("/DeleteUser/{ids}")
    @ApiOperation(value = "删除业务模块用户")
    @Log(OperationType = OperationType.DeleteEntity)
    @Permission(permission = "user:nonmodule:grant")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult deleteUser(@PathVariable("ids") String ids) {
        List<String> array = Arrays.asList(ids.split(","));
        return ActionResult.success(this.bizUserService.removeByIds(array));
    }

    //endregion

    //region 统一认证用户登录

    @PostMapping("/Logon")
    @ApiOperation(value = "统一认证用户登录")
    @AllowAnonymous
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult add(@RequestBody @Valid LoginDTO loginDTO) throws Exception {
        return this.bizUserModuleService.logon(loginDTO);
    }

    //endregion

    //region 修改业务用户密码

    @PostMapping("/UpdatePassword")
    @ApiOperation(value = "修改用户密码")
    @AllowAnonymous
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult updatePassword(@RequestBody @Valid UnifiedAuthorizationDTO authorizationDTO) throws Exception {
        authorizationDTO.setToken(RuYiRsaUtil.decrypt(authorizationDTO.getToken(),
                this.systemConfig.getRsaPrivateKey()));
        authorizationDTO.setUserPassword(RuYiRsaUtil.decrypt(authorizationDTO.getUserPassword(),
                this.systemConfig.getRsaPrivateKey()));

        String[] array = authorizationDTO.getUserPassword().split("_");
        authorizationDTO.setUserId(array[1]);
        authorizationDTO.setUserPassword(array[0]);

        String key = this.systemRedisConfig.getUnifiedAuthenticationPattern() + authorizationDTO.getUserId()
                + "_" + authorizationDTO.getToken();
        String pattern = String.format("%s*", key);
        List<String> keys = this.redisUtils.scan(pattern);
        if (keys.size() > 0) {
            BizUser bizUser = this.bizUserService.getById(authorizationDTO.getUserId());
            if (bizUser != null) {
                String salt = UUID.randomUUID().toString();
                bizUser.setSalt(salt);
                String aesKey = this.systemConfig.getAesKey();
                authorizationDTO.setUserPassword(RuYiAesUtil.encrypt(authorizationDTO.getUserPassword() + salt, aesKey));
                bizUser.setUserPassword(authorizationDTO.getUserPassword());
                bizUser.setModifier(authorizationDTO.getUserId());
                this.bizUserService.updateById(bizUser);
                return ActionResult.ok();
            } else {
                throw new RuYiAdminCustomException("user is not exited");
            }
        } else {
            throw new RuYiAdminCustomException("token is not exited");
        }
    }

    //endregion

}
